import bean.*;
import com.alibaba.fastjson.JSON;
import okhttp3.*;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Type;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * Created by shiyanqi on 19/4/8.
 */
public class RemoteApiClient {

    public static final String Host = "http://open.guangfen.net/api/v46";
    //先签名秘钥配置
    public static final String APP_SECRET = "";

    public interface CallBack<T>{
        void onSuccess(T data) throws Exception;

        void onFailure(String message);
    }

    private static OkHttpClient okHttpClient = new OkHttpClient.Builder().connectTimeout(5, TimeUnit.MINUTES).readTimeout(5, TimeUnit.MINUTES).build();


    interface Api {
        String Micropay = "/pay/micropay";
        String Unifiedorder = "/pay/unifiedorder";
        String Create = "/pay/create";
        String Query = "/pay/query";
        String Reverse = "/pay/reverse";
        String Refund = "/pay/refund";
        String Refund_query = "/pay/refund_query";
        String Ticket_query = "/ticket/query";
        String Ticket_use = "/ticket/use";
    }

    public static void Unifiedorder(String app_id, String store_code, String dev_id, String oper_id, String pay_channel, Integer amount, Integer undiscountable_amount, String raw_data, String back_notify_url, String down_trade_no, String subject, final CallBack responseHandler) {

        long time = System.currentTimeMillis();
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date d1 = new Date(time);
        String timestamp = format.format(d1);

        Map<String, String> params = new HashMap<>();
        params.put("app_id", app_id);
        params.put("version", "v46");
        params.put("timestamp", timestamp);
        params.put("store_code", store_code);
        params.put("dev_id", dev_id);
        params.put("oper_id", oper_id);
        params.put("pay_channel", pay_channel);
        params.put("amount", amount.toString());
        params.put("undiscountable_amount", undiscountable_amount.toString());
        params.put("raw_data", raw_data);
        params.put("back_notify_url", back_notify_url);
        params.put("down_trade_no", down_trade_no);
        params.put("subject", subject);
        post(Api.Unifiedorder, params, responseHandler, TradeBean.class);
    }


    public static void Micropay(String app_id, String store_code, String dev_id, String oper_id, Integer amount, Integer undiscountable_amount, String raw_data, String auth_code, String down_trade_no, String subject, final CallBack responseHandler) {

        long time = System.currentTimeMillis();
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date d1 = new Date(time);
        String timestamp = format.format(d1);

        Map<String, String> params = new HashMap<>();
        params.put("app_id", app_id);
        params.put("version", "v46");
        params.put("timestamp", timestamp);
        params.put("store_code", store_code);
        params.put("dev_id", dev_id);
        params.put("oper_id", oper_id);
        params.put("amount", amount.toString());
        params.put("undiscountable_amount", undiscountable_amount.toString());
        params.put("raw_data", raw_data);
        params.put("auth_code", auth_code);
        params.put("down_trade_no", down_trade_no);
        params.put("subject", subject);
        post(Api.Micropay, params, responseHandler, TradeBean.class);
    }

    public static void Create(String app_id, String store_code, String dev_id, String oper_id, String pay_channel, Integer amount, Integer undiscountable_amount, String raw_data,
                              String back_notify_url, String down_trade_no, String subject, String sub_appid, String sub_appid_type, String sub_open_id, final CallBack responseHandler) {
        long time = System.currentTimeMillis();
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date d1 = new Date(time);
        String timestamp = format.format(d1);

        Map<String, String> params = new HashMap<>();
        params.put("app_id", app_id);
        params.put("version", "v46");
        params.put("timestamp", timestamp);
        params.put("store_code", store_code);
        params.put("dev_id", dev_id);
        params.put("oper_id", oper_id);
        params.put("pay_channel", pay_channel);
        params.put("amount", amount.toString());
        params.put("undiscountable_amount", undiscountable_amount.toString());
        params.put("raw_data", raw_data);
        params.put("back_notify_url", back_notify_url);
        params.put("down_trade_no", down_trade_no);
        params.put("subject", subject);
        params.put("sub_appid", sub_appid);
        params.put("sub_appid_type", sub_appid_type);
        params.put("sub_open_id", sub_open_id);
        post(Api.Create, params, responseHandler, TradeBean.class);
    }




    public static void Query(String app_id, String store_code, String pay_channel, String down_trade_no, String trade_no, String trade_no_type, final CallBack responseHandler) {
        long time = System.currentTimeMillis();
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date d1 = new Date(time);
        String timestamp = format.format(d1);
        Map<String, String> params = new HashMap<>();
        params.put("app_id", app_id);
        params.put("version", "v46");
        params.put("timestamp", timestamp);
        params.put("store_code", store_code);
        params.put("down_trade_no", down_trade_no);
        params.put("trade_no", trade_no);
        params.put("pay_channel", pay_channel);
        params.put("trade_no_type", trade_no_type);
        post(Api.Query, params, responseHandler, TradeBean.class);
    }

    public static void Reverse(String app_id, String store_code, String pay_channel, String down_trade_no, String trade_no, String trade_no_type, final CallBack responseHandler) {
        long time = System.currentTimeMillis();
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date d1 = new Date(time);
        String timestamp = format.format(d1);
        Map<String, String> params = new HashMap<>();
        params.put("app_id", app_id);
        params.put("version", "v46");
        params.put("timestamp", timestamp);
        params.put("store_code", store_code);
        params.put("down_trade_no", down_trade_no);
        params.put("trade_no", trade_no);
        params.put("pay_channel", pay_channel);
        params.put("trade_no_type", trade_no_type);
        post(Api.Reverse, params, responseHandler, TradeBean.class);
    }

    public static void Refund(String app_id, String store_code, String pay_channel, String trade_no, String refund_down_trade_no, String refund_reason, String refund_oper_id, String terminal_code, Integer refund_amount, final CallBack responseHandler) {
        long time = System.currentTimeMillis();
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date d1 = new Date(time);
        String timestamp = format.format(d1);
        Map<String, String> params = new HashMap<>();
        params.put("app_id", app_id);
        params.put("version", "v46");
        params.put("timestamp", timestamp);
        params.put("store_code", store_code);
        params.put("refund_oper_id", refund_oper_id);
        params.put("terminal_code", terminal_code);
        params.put("refund_amount", refund_amount.toString());
        params.put("refund_down_trade_no", refund_down_trade_no);
        params.put("trade_no", trade_no);
        params.put("pay_channel", pay_channel);
        params.put("refund_reason", refund_reason);
        post(Api.Refund, params, responseHandler, RefundTradeBean.class);
    }

    public static void RefundQuery(String app_id, String store_code, String pay_channel, String trade_no, String refund_down_trade_no, String refund_trade_no, final CallBack responseHandler) {
        long time = System.currentTimeMillis();
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date d1 = new Date(time);
        String timestamp = format.format(d1);
        Map<String, String> params = new HashMap<>();
        params.put("app_id", app_id);
        params.put("version", "v46");
        params.put("timestamp", timestamp);
        params.put("store_code", store_code);
        params.put("refund_down_trade_no", refund_down_trade_no);
        params.put("trade_no", trade_no);
        params.put("pay_channel", pay_channel);
        params.put("refund_trade_no", refund_trade_no);
        post(Api.Refund_query, params, responseHandler, RefundTradeBean.class);
    }

    public static void TicketQuery(String app_id, String store_code, String pay_channel, String dev_id, String oper_id, String ticket_code, final CallBack responseHandler) {
        long time = System.currentTimeMillis();
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date d1 = new Date(time);
        String timestamp = format.format(d1);
        Map<String, String> params = new HashMap<>();
        params.put("app_id", app_id);
        params.put("version", "v46");
        params.put("timestamp", timestamp);
        params.put("store_code", store_code);
        params.put("dev_id", dev_id);
        params.put("oper_id", oper_id);
        params.put("pay_channel", pay_channel);
        params.put("ticket_code", ticket_code);
        post(Api.Ticket_query, params, responseHandler, TicketBean.class);
    }

    public static void TicketUse(String app_id, String store_code, String pay_channel, String dev_id, String oper_id, String ticket_code, String ticket_count, final CallBack responseHandler) {
        long time = System.currentTimeMillis();
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date d1 = new Date(time);
        String timestamp = format.format(d1);
        Map<String, String> params = new HashMap<>();
        params.put("app_id", app_id);
        params.put("version", "v46");
        params.put("timestamp", timestamp);
        params.put("store_code", store_code);
        params.put("dev_id", dev_id);
        params.put("oper_id", oper_id);
        params.put("pay_channel", pay_channel);
        params.put("ticket_code", ticket_code);
        params.put("ticket_count", ticket_count);
        post(Api.Ticket_use, params, responseHandler, TicketBean.class);
    }



    public static <T> void post(String url, Map<String, String> params, final CallBack<T> callBack, Type type) {
        FormBody.Builder builder = new FormBody.Builder();
        for (Map.Entry<String, String> entry : params.entrySet()) {
            builder.add(entry.getKey(), entry.getValue());
        }
        builder.add("sign", generateSignature(params));
        FormBody formBody = builder.build();
        Request request = new Request.Builder()
                .url(Host + url)
                .post(formBody)
                .build();
        Call call = okHttpClient.newCall(request);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {

            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                String result = response.body().string();
                BaseBean bean = new BaseBean();
                bean.parse(result);
                if (bean.getCode().equals("-1")) {
                    callBack.onFailure(bean.getMsg());
                } else if (bean.isOk()) {
                    //检查代码
                    try {
                        T t = JSON.parseObject(bean.getData(), type);
                        callBack.onSuccess(t);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }else {
                    System.out.print(bean.getMsg());
                }

            }
        });
    }

    public static String generateSignature(final Map<String, String> data) {
        Set<String> keySet = data.keySet();
        String[] keyArray = keySet.toArray(new String[keySet.size()]);
        Arrays.sort(keyArray);
        StringBuilder sb = new StringBuilder();
        for (String k : keyArray) {
            if (data.get(k).trim().length() > 0) // 参数值为空，则不参与签名
                sb.append(k).append("=").append(data.get(k).trim()).append("&");
        }
        sb.append("APP_SECRET=").append(APP_SECRET);
        System.out.println(sb.toString());
        String result = md5(sb.toString()).toUpperCase();
        System.out.println(result);
        return result;
    }

    private static String md5(String string) {
        byte[] hash;
        try {
            hash = MessageDigest.getInstance("MD5").digest(string.getBytes("UTF-8"));
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("Huh, MD5 should be supported?", e);
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException("Huh, UTF-8 should be supported?", e);
        }

        StringBuilder hex = new StringBuilder(hash.length * 2);
        for (byte b : hash) {
            if ((b & 0xFF) < 0x10) hex.append("0");
            hex.append(Integer.toHexString(b & 0xFF));
        }
        return hex.toString();
    }


}
